#include "Monticulo.h"

#include <memory>
#include <vector>

#include <SOIL.h>

#include "Algoritmo2D.h"
#include "Bezier2D.h"


Monticulo::Monticulo() : 
	m_superficie( std::unique_ptr< Algoritmo2D >( new Bezier2D(3, 3) ) )
{
	std::vector< glm::vec3 > vec1;
	vec1.push_back( glm::vec3(1.0f, -1.0f, 0.0f) );
	vec1.push_back( glm::vec3(1.0f, 0.0f, 0.0f) );
	vec1.push_back( glm::vec3(1.0f, 1.0f, 0.0f) );

	std::vector< glm::vec3 > vec2;
	vec2.push_back( glm::vec3(0.0f, -1.0f, 0.0f) );
	vec2.push_back( glm::vec3(0.0f, 0.0f, 5.0f) );
	vec2.push_back( glm::vec3(0.0f, 1.0f, 0.0f) );

	std::vector< glm::vec3 > vec3;
	vec3.push_back( glm::vec3(-1.0f, -1.0f, 0.0f) );
	vec3.push_back( glm::vec3(-1.0f, 0.0f, 0.0f) );
	vec3.push_back( glm::vec3(-1.0f, 1.0f, 0.0f) );

	this->m_superficie.addCurvaControl( vec1 );
	this->m_superficie.addCurvaControl( vec2 );
	this->m_superficie.addCurvaControl( vec3 );
}


Monticulo::~Monticulo() {
	glDeleteTextures(1, &this->m_textureID);
}


void Monticulo::init(const glm::vec3 &posFuenteLuzDireccional, std::string vertShaderFileName, std::string fragShaderFileName) {
	this->m_superficie.buildCurva(18, 12);
	Renderizable::init(posFuenteLuzDireccional, "TextureShader.vert", "TextureShader.frag");

	// Carga la textura
	int alto;
	int ancho;
	int canales;
	unsigned char* imgBuffer = SOIL_load_image("Rocas.jpg", &ancho, &alto, &canales, SOIL_LOAD_RGBA);

	// Genera un buffer de textura donde se guarda la imagen
	glGenTextures(1, &this->m_textureID);
	glBindTexture(GL_TEXTURE_2D, this->m_textureID);

	// Asocia la textura con el buffer
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, ancho, alto, 0, GL_RGBA, GL_UNSIGNED_BYTE, imgBuffer);

	// Aplica "linear filtering" a la textura, tambien evita un problema con la falta de mipmaps en la textura
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

	// Esta es otra forma de evitar los errores de mipmaps
	//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
	//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);

	// Borra la imagen de la memoria, ya esta cargada en la placa de video
	delete[] imgBuffer;
}


void Monticulo::renderizar(const glm::mat4 &matVista, const glm::mat4 &matProyeccion, const glm::mat4 &matModelo) const {
	// Carga el shader
	GLuint shaID = Renderizable::cargarShaderProgram();

	// Empieza a dibujar todo
	glEnableClientState(GL_VERTEX_ARRAY);
	glEnableClientState(GL_NORMAL_ARRAY);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);

	// Los datos de iluminacion
	Renderizable::cargarDatosIluminacion(matModelo);

	// Las matrices de transformacion
	Renderizable::cargarMatricesTransformacion(matVista, matProyeccion, matModelo);


	// Activa la unidad de textura 0 y asocia a la misma la textura que fue inicializada
	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, this->m_textureID);

	// Pasa los datos de la unidad de textura al shader
	const GLint locTextura = glGetUniformLocation(shaID, "textura");
	if (locTextura != -1){
		glUniform1i(locTextura, 0);
	}


	// Los datos de la superficie
	std::vector< glm::vec3 > vecVertices = this->m_superficie.getVertices();
	std::vector< glm::vec3 > vecNormales = this->m_superficie.getNormales();
	std::vector< glm::vec2 > vecCoordsTextura = this->m_superficie.getCoordenadasTextura();
	std::vector< unsigned int > vecIndexBuffer = this->m_superficie.getIndexBuffer();

	glVertexPointer(3, GL_FLOAT, 0, &vecVertices[0]);
	glNormalPointer(GL_FLOAT, 0, &vecNormales[0]);
	glTexCoordPointer(2, GL_FLOAT, 0, &vecCoordsTextura[0]);
	glDrawElements(GL_TRIANGLES, vecIndexBuffer.size(), GL_UNSIGNED_INT, &vecIndexBuffer[0]);

	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	glDisableClientState(GL_NORMAL_ARRAY);
	glDisableClientState(GL_VERTEX_ARRAY);
}


